﻿using System;
using System.Diagnostics;
using System.Runtime.Serialization;

namespace Ns
{
    /// <summary>
    /// Represents the prefix of an <see cref="Unit"/>.
    /// </summary>
#if !SILVERLIGHT
    [Serializable]
#endif 
    public struct UnitPrefix
        : IEquatable<UnitPrefix>
        , IComparable<UnitPrefix>
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly string _symbol;

        /// <summary>
        /// Gets the symbol of the prefix.
        /// </summary>
        /// <value>The prefix symbol.</value>
        public string Symbol
        {
            get { return _symbol; }
        }

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly int _exponent;

        /// <summary>
        /// Gets the exponent of the prefix.
        /// </summary>
        /// <value>The prefix exponent.</value>
        public int Exponent
        {
            get { return _exponent; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="UnitPrefix"/> struct.
        /// </summary>
        /// <param name="symbol">The symbol.</param>
        /// <param name="exponent">The exponent.</param>
        public UnitPrefix(string symbol, int exponent)
        {
            _symbol = symbol;
            _exponent = exponent;
        }

        #region Overriden Members

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return _symbol;
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            int hash = 0x4043ed47;
            hash = (hash * -1521134295) + Exponent.GetHashCode();
            hash = (hash * -1521134295) + (Symbol != null ? 0 : Symbol.GetHashCode());
            return hash;
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        /// 	<b>true</b> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <b>false</b>.
        /// </returns>
        public override bool Equals(object obj)
        {
            return (obj is UnitPrefix)
                ? Equals((UnitPrefix)obj)
                : false;
        }

        #endregion

        #region IEquatable<UnitPrefix> Members

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(UnitPrefix other)
        {
            return object.Equals(Exponent, other.Exponent)
                && string.Equals(Symbol, other.Symbol, StringComparison.InvariantCulture);
        }

        #endregion

        #region IComparable<UnitPrefix> Members

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings:
        /// Value
        /// Meaning
        /// Less than zero
        /// This object is less than the <paramref name="other"/> parameter.
        /// Zero
        /// This object is equal to <paramref name="other"/>.
        /// Greater than zero
        /// This object is greater than <paramref name="other"/>.
        /// </returns>
        public int CompareTo(UnitPrefix other)
        {
            return Exponent.CompareTo(other.Exponent);
        }

        #endregion

        #region Operators

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(UnitPrefix x, UnitPrefix y)
        {
            return x.Equals(y);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(UnitPrefix x, UnitPrefix y)
        {
            return !x.Equals(y);
        }

        /// <summary>
        /// Performs an explicit conversion from <see cref="System.String"/> to <see cref="Ns.UnitPrefix"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        /// <exception cref="ArgumentException"><paramref name="value"/> is not representing a prefix.</exception>
        public static explicit operator UnitPrefix(string value)
        {
            //first consider special cases
            if (string.IsNullOrEmpty(value))
                return UnitPrefixes.Default;
            if (string.Equals("da", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Deca;

            //then consider normal cases
            if (string.Equals("y", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Yocto;
            if (string.Equals("z", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Zepto;
            if (string.Equals("a", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Atto;
            if (string.Equals("f", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Femto;
            if (string.Equals("p", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Pico;
            if (string.Equals("n", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Nano;
            if (string.Equals("µ", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Micro;
            if (string.Equals("m", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Milli;
            if (string.Equals("c", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Centi;
            if (string.Equals("d", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Deci;
            if (string.Equals("h", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Hecto;
            if (string.Equals("k", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Kilo;
            if (string.Equals("M", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Mega;
            if (string.Equals("G", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Giga;
            if (string.Equals("T", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Tera;
            if (string.Equals("P", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Peta;
            if (string.Equals("E", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Exa;
            if (string.Equals("Z", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Zetta;
            if (string.Equals("Y", value, StringComparison.InvariantCulture))
                return UnitPrefixes.Yotta;

            throw new ArgumentException("Unit prefix not found.", "value");
        }

        #endregion
    }
}